from flask_wtf import FlaskForm
from wtforms import StringField,PasswordField,SubmitField,BooleanField,TextAreaField
from wtforms.validators import Length,EqualTo,Email,ValidationError,DataRequired # 视频里WTForms版本2.1，我的2.3.3，视频里能直接运行，我的报错Exception: Install 'email_validator' for email validation support.按照提示安装email_validator后能运行，也有人说退回低版本
from app.models import User # 从app文件夹里models.py中导入User类
from flask_wtf.file import FileField,FileAllowed,FileRequired
from app.extensions import pictures # 从app文件夹里extensions.py中导入pictures对象
from flask_login import current_user
from flask import session


class RegisterForm(FlaskForm): # 注册页表单
    username=StringField('用户名:',validators=[Length(6,20,message='用户名只能在6-20位之间')],render_kw={'placeholder':'请输入6-20位之间的用户名'}) # render_kw是渲染关键字，传入字典，键是属性名，值是属性值。这里写的内容类似html里给input框设置placeholder属性。
    password=PasswordField('密码:',validators=[Length(6,18,message='密码只能在6-18位之间')],render_kw={'placeholder':'请输入6-18位之间的密码'})
    password_confirm=PasswordField('确认密码:',validators=[EqualTo('password',message='两次输入的密码不一致')])
    email=StringField('邮箱:',validators=[Email(message='邮箱格式不正确')])
    phone=StringField('手机号:') # 通过Ajax前端验证输入的内容是否合法，可设置失去焦点时验证，用户体验更好，不用wtforms.validators中的验证
    recaptcha=StringField('验证码:',validators=[DataRequired(message='验证码不能为空')])
    submit=SubmitField('注册')

    # 点击注册按钮后才执行自定义验证。另一个验证方式是Ajax前端验证，可设置失去焦点时验证，用户体验更好，验证手机号处(user.py中路由verify_phone和register.html中)使用了
    def validate_username(self,field): # 自定义用户名验证，不写的话重复注册时报错。自定义验证器，函数名以validate_开头，后跟要验证的字段，验证时会根据函数名自动判断要验证的字段，传的参数是要验证的字段。
        if User.query.filter(User.username==field.data).first():
            raise ValidationError('该用户名已被使用，请更换用户名') # 验证失败时需抛出异常。这里用户名不能相同，相同就是失败
        return True # 验证通过时返回True

    def validate_email(self,field): # 自定义邮箱验证
        if User.query.filter(User.email==field.data).first():
            raise ValidationError('该邮箱已被使用，请更换邮箱')
        return True

    def validate_recaptcha(self,field):
        input_code=field.data
        true_code=session.get('verification_code')
        if input_code.lower()!=true_code.lower():
            raise ValidationError('验证码错误')


class LoginForm1(FlaskForm): # 密码登录页表单
    username=StringField('用户名:',validators=[DataRequired(message='用户名不能为空')])
    password=PasswordField('密码:',validators=[DataRequired(message='密码不能为空')])
    recaptcha=StringField('验证码:',validators=[DataRequired(message='验证码不能为空')])
    remember=BooleanField('一周内自动登录') # 用Flask-Login时添加的，没用Flask-Login时靠手动设置session有效期
    submit=SubmitField('登录')

    def validate_recaptcha(self,field):
        input_code=field.data
        true_code=session.get('verification_code')
        if input_code.lower()!=true_code.lower():
            raise ValidationError('验证码错误')


class LoginForm2(FlaskForm): # 短信登录页表单
    phone=StringField('手机号:',validators=[DataRequired(message='手机号不能为空')])
    verification_code=StringField('验证码:',validators=[DataRequired(message='验证码不能为空')])
    remember=BooleanField('一周内自动登录')
    submit=SubmitField('登录')


class UploadPortraitForm(FlaskForm): # 上传头像表单
    portrait=FileField('上传头像:',validators=[FileRequired('未选择文件'),FileAllowed(pictures,'只能上传图片')])
    submit=SubmitField('上传')


class ModifyPasswordForm(FlaskForm): # 修改密码表单
    old_password=PasswordField('旧密码:',validators=[DataRequired(message='密码不能为空')])
    new_password=PasswordField('新密码:',validators=[Length(6,18,message='密码只能在6-18位之间')])
    password_confirm=PasswordField('确认密码:',validators=[EqualTo('new_password',message='两次输入的密码不一致')])
    submit=SubmitField('提交')

    def validate_old_password(self,field):
        u=User.query.filter(User.username==current_user.username).first()
        if u.verify_password(field.data): # 输入的旧密码加密后和数据库中的一致
            return True
        raise ValidationError('旧密码输入错误，请重新输入')

    def validate_new_password(self,field): # 不用这个验证器的话，也可以用user.py中修改密码路由中注释掉的验证代码，那里个人觉得是验证前端数据，所以不安全
        # if field.data==self.old_password.data: # 不能用ModifyPasswordForm.old_password.data，报错。个人觉得是验证前端数据的写法，不如下面对比数据库数据安全
        u=User.query.filter(User.username == current_user.username).first()
        if u.verify_password(field.data): # 输入的值加密后和数据库中一致，说明新旧密码相同
            raise ValidationError('不能与旧密码相同')
        return True


class PublishArticleForm(FlaskForm): # 发布文章表单(非富文本方式)
    title=StringField('标题',validators=[Length(2,100,message='标题只能在2-100位之间')],render_kw={'placeholder':'标题字数2-100'})
    content=TextAreaField('内容',validators=[Length(1,10000,message='正文只能在1-10000位之间')],render_kw={'placeholder':'正文字数1-10000'})
    submit=SubmitField('发布')